//
// AppInterface.cpp
// libprotea
// Tools Library for the Protea Project / Application Handler Interface
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
// AppInterface.cpp : Implementation of XCAppInterface
//
#include "../defines.h"
#include "AppInterface.h"
#include "Transactions.h"
#include "security_helper.h"

#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <math.h>

#include "../errorhandler.h"
#include "../Appdic/appdic.h"
#include "../Buffer/Buffer.h"
#include "../Database/DBInterface.h"
#include "../Fields/fields.h"
#include "../OSDep/port.h"
#include "../OSDep/process.h"
#include "../OSDep/shlib.h"
#include "../OSDep/files.h"
#include "../OSDep/dir.h"
#include "../OSDep/registry.h"
#include "../OSDep/misc.h"

#include "../defns.h"
using namespace ProteaApp1;

#include <sys/types.h>

#define SAFEDELETE(x) if (x) { delete [] x; x = NULL; };


const long LOGGING_PORT = 4025;

// HACK: FIX THIS
void XCAppInterface::DebugMessageTraffic( char* string, XCMsgPortPtr port ) {
	char result[ 2048 ];
	char* temp = result;
	char* type = "UNKNOWN";

	if (!port)
		return;

	temp += sprintf( temp, "%s  %s ", process, string );
	switch( port->porttype ) {
		case pt_misc:		type = "Misc";				break;
		case pt_listen:		type = "Listener";			break;
		case pt_gcs:		type = "GCS";				break;
		case pt_sub:		type = "Substation";		break;
		case pt_disp:		type = "Dispatcher";		break;
		case pt_app:		type = "Application";		break;
		case pt_ui:			type = "User Interface";	break;
	};
	temp += sprintf( temp, "%s", type );

	if (port == parent)
		temp += sprintf( temp, "  [PARENT]" );
	else if (port == sockin)
		temp += sprintf( temp, "  [LISTENER]" );

	ToolsTrace( TRC_MESSAGING, TLV_LOW, result );
};




XCAppOptions::XCAppOptions()
{
	version = 0;
	no_database = false;
	appdic_first_run = false;
	debug_type_filter = 0;
	for ( int i = 0; i < num_tc; i++ )
		debug_level_cutoff[ i ] = 0;
};

/////////////////////////////////////////////////////////////////////////////
// XCAppInterface
//
XCAppInterface::XCAppInterface() {
	process = NULL;
	worker_threads = 1;
	perf_counters = NULL;

	trans_open = 0;
	trans_complete = 0;
	trans_stated = 0;

	msg_sent = 0;
	msg_recv = 0;

	tracing = true;

	transaction_defn_list_count = 0;
	transaction_defn_list = NULL;

	transactions_list_count = 0;
	transactions_list = NULL;

	supertrans = NULL;

	appl_defn_list_count = 0;
	appl_defn_list = NULL;

	memset( &options, 0, sizeof( options ) );
	for ( int i = 0; i < num_tc; i++ )
		options.debug_level_cutoff[ i ] = TLV_EVERYTHING;

	process_storage = new char[ 200 ];

	uelib = NULL;
	db = NULL;
	logging = NULL;
	logfile = NULL;
	func_level = 0;

	// Set up Traffic
	abort.Reset();
	message_loop_running = false;

	process_list = NULL;
	parent = NULL;
	childlist = NULL;
	sockin = NULL;

	TxnList = NULL;
	StatedTxnList = new XCStatedTxnList();
	StatedTxnList->App = this;

	mynum = rand();
	parentpid = 0;

	XCRegistry registry;
	try {
		root = registry.GetStringValue( "XSIROOT" );
	} catch( XCToolsError& error ) {
		// On error, set the root to NULL
		root = NULL;
	};

	if (root) {
		char* temp;
		int len = strlen( root );
		if (strcmp(&root[len-strlen(OSFILEDELIM)], OSFILEDELIM)==0) {
			// Make an exact copy
			temp = new char[ len+1 ];
			strcpy( temp, root );
			free(root);
			root = temp;
		} else {
			// Make a copy and append a delimiter
			temp = new char[ len+1+strlen(OSFILEDELIM) ];
			strcpy( temp, root );
			strcat( temp, OSFILEDELIM );
			free( root );
			root = temp;
		};

		sprintf( ToolsDir, "%sTools%sappdic%s", root, OSFILEDELIM, OSFILEDELIM );
	} else
		*ToolsDir = 0;

	system_values_buf = NULL;
	for (int i=0; i<MAX_LANGUAGES; i++)
		literal_values_buf[i] = NULL;

	APPDIC_DATA = NULL;
};


XCAppInterface::~XCAppInterface() {
	INFUNC( XCAppInterface::~XCAppInterface, this );

	Uninit();

	if (uelib)
		delete uelib;

	delete StatedTxnList;

	delete [] process_storage;

	// Delete all of our open connections
	ClearPorts( true );

	StopLogging();

	// Delete all performance counters
	while (perf_counters) {
		tPerfCounters* temp = perf_counters->next;
		delete perf_counters;
		perf_counters = temp;
	};

	if (root)
		delete [] root;

	if (process)
		free( process );

	OUTFUNC();
};



void XCAppInterface::Uninit() {
	INFUNC( XCAppInterface::Uninit, this );

	if (db) {
		delete db;
		db = NULL;
	};

	CleanupObjects( 1, &system_values_buf );
	for (int i=0; i<MAX_LANGUAGES; i++)
		CleanupObjects( 1, &literal_values_buf[i] );

	SAFEDELETE( transaction_defn_list );
	SAFEDELETE( transactions_list );
	SAFEDELETE( appl_defn_list );
	SAFEDELETE( APPDIC_DATA );

	OUTFUNC();
};




void XCAppInterface::StopLogging() {
	if (logging) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Close logging" );
		logging->Close();
		delete logging;
		logging = NULL;
	};

	if (logfile) {
		logfile->close();
		delete logfile;
		logfile = NULL;
	};
};



void XCAppInterface::StartLogging( char* process_name ) {
	if (!logging) {
		// Turn on logging
		try {
/*
			logging = new XCSocketTransport( NULL );
			logging->Create( AF_INET, SOCK_DGRAM, 0 );
			logging->SetBroadcast( true );
			logging->Connect( "192.168.2.255", LOGGING_PORT );
*/
			logging = NULL;
		} catch( ... ) {
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Unable to create logging socket: %d", errno );
			delete logging;
			logging = NULL;
			return;
		}; 
	};

	if (!logfile) {
		// open log file
		char filename[ MAX_PATH ];
		sprintf( filename, "%s/Logs/trace.%d.txt", root, XCGetProcessIdentifier() );

		logfile = new ofstream;
		logfile->open( filename, ios::out );

		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "*!* %s *!*", process_name );
	};
};



void XCAppInterface::SendStatus( route_loc src, XCMsgPortPtr parent, long listener ) {
	char hostname[ 256 ];

	if (parent) {
		XCGetHostName( hostname, 255 );
		XCToolsMessage stat( this );
		stat.CreateMsg( STATUS_MESSAGE_SIZE + strlen( hostname ) + 1);
		stat.SetHeader( msg_status, src, dest_gcs, false, false );
		status_message* stat_info = (status_message*)stat.GetDataPtr();
		if (process)
			strcpy( stat_info->filename, process );
		else
			strcpy( stat_info->filename, "UNKNOWN" );
		strcpy( stat_info->hostname, hostname );
		stat_info->pid = XCGetProcessIdentifier();
		stat_info->listenport = listener;
		stat_info->parentpid = parentpid;
		stat_info->start = true;
		stat_info->trans_complete = trans_complete;
		stat_info->trans_open = trans_open;
		stat_info->trans_stated = trans_stated;

		stat_info->msg_sent = msg_sent;
		stat_info->msg_recv = msg_recv;

		stat_info->debug_type_filter = options.debug_type_filter;
		for (int i=0; i<num_tc; i++) {
			stat_info->debug_level_cutoff[i] = options.debug_level_cutoff[i];
		};

//		printf( "Send status: %s, trans_comp = %d\n", stat_info->filename, stat_info->trans_complete );

		SendMessage( msg_src, parent, &stat );
	};
};


 
int XCAppInterface::Initialize(char* process_name, int app_number, char* FileExtension)
{
	StartLogging( process_name );

	process = strdup( process_name );

	DO_SDK_SECURITY_CHECK;

	EXT_INFUNC( XCAppInterface::Initialize, this )
	char filename[ MAX_PATH ];
	long loc;

	APP_INSTALL_EXCEPTIONS;

	myappnum = (int) fabs( (float)app_number );
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Init appinterface %d (Application %d : %d)", mynum, app_number, myappnum );

	Uninit();

#	ifdef CHECK_MEM
	APP_INIT_DEBUG;
#	endif

	// Check to see that our root variable was defined
	if ( *ToolsDir == 0 )
	{
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "$XSIROOT not set : Invalid Installation"  );
		RAISETOOLSERROR( ERR_BAD_XSIROOT );
	};

	// Load in all 'APPDIC' information
	strcpy( filename, ToolsDir );
	strcat( filename, OSFILEDELIM );
	loc = strlen( filename );

	// Setup location of the error file
	strcpy( errloc, filename );

	if (myappnum) {
		XCFileTransport AppLoader( this );

		sprintf( &filename[loc], "APPDIC%d.Appdic", myappnum );
		if (FileExtension)
			strcat( filename, FileExtension );

		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Loading: %s", filename );

		AppLoader.Open( filename );
		APPDIC_DATA_SIZE = AppLoader.GetSize();
		APPDIC_DATA = new char[ APPDIC_DATA_SIZE ];
		appdic_header = (buffer_header*)APPDIC_DATA;
		AppLoader.Recv( APPDIC_DATA, APPDIC_DATA_SIZE );
		AppLoader.Close();

		if (!options.appdic_first_run) {

			// Initialize the security system
			sprintf( &filename[loc], "security.xml" );
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Loading: %s", filename );

			// Do we need to initialize security?
			if (!options.no_transactions)
				AuthInit( filename );

			// If we don't open a database, we don't need the transactions list
//			if (!options.no_database) {
				sprintf( &filename[loc], "Transaction_Defn%d.Appdic", myappnum );
				if (FileExtension)
					strcat( filename, FileExtension );
				transaction_defn_list_count = LoadFile( filename, (void**)&transaction_defn_list, sizeof( transaction_defn ));
//			};
		};
	};


	if ( !myappnum || app_number < 0 )
	{
		// load these if no application number or app_number is negative (GCS, etc.)
		if (!options.appdic_first_run) {
			sprintf( &filename[loc], "Appl_Defn.Appdic" );
			if (FileExtension)
				strcat( filename, FileExtension );
			if (!(appl_defn_list_count = LoadFile( filename, (void**)&appl_defn_list, sizeof( appl_defn ))))
				RAISETOOLSERROR_1PARAM( ERR_BAD_INIT, filename );
		};
	};

	// Clear out our values
	system_values_buf = NULL;
	for (int i=0; i<MAX_LANGUAGES; i++)
		literal_values_buf[i] = NULL;

	// Create system buffers
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Starting app: %d", myappnum );

	try {
		if (myappnum > 1) {
			system_values_buf = CreateBuffer( 10000, true );
			system_values_buf->SetReadOnly( true );
		};

		// Read in the literal values buffers for the multiple languages
		for (int i=0; i<MAX_LANGUAGES; i++) {
			literal_values_buf[i] = CreateBuffer( 10001 + i, true );
			literal_values_buf[i]->SetReadOnly( true );
		};
	} catch( XCToolsError& error ) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Error creating literals/system values" );
		error.Clear();
	};

	// init DBHandler
	if (!options.no_database) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Initialize database system" );
		db = new XCDBHandler();		// create database handler
		db->Initialize( this );
	};

	// Initialize user exits
	if (!uelib)
		InitializeUE();

	EXT_OUTFUNCRET( 0 );
}



bool XCAppInterface::InitializeUE() {
	uelib = new XCSharedLibrary();
	try {
		char ue_path[ MAX_PATH ];
		sprintf( ue_path, "%s%sTools%s%s%suesl.so", root, OSFILEDELIM, OSFILEDELIM, process, OSFILEDELIM, OSFILEDELIM );
		uelib->Open( ue_path );
		return true;
	} catch( XCToolsException& error ) {
		error.Clear();
		delete uelib;
		uelib = NULL;
		ToolsTrace( TRC_USEREXIT, TLV_HIGH, "UE library not loaded..." );
	};

	return false;
};


int XCAppInterface::LoadFile(char *filename, void **data, int recordsize) {
	fstream input;
	long size;

	if (!XCFileExists( filename ))
		return 0;

	input.open( filename,ios::binary | ios::in );
	if (input.bad() || input.fail())
		return 0;

	input.seekg( 0, ios::end );
	size = input.tellg();
	input.seekg( 0, ios::beg );

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Loading %s, %d records", filename, size/recordsize );

	*data = new char[ size ];

	input.read( (char*)*data, size );
	input.close();

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Loaded %s, %d records", filename, size/recordsize );

	return size / recordsize; 
}








buffer_format* XCAppInterface::GetFormatInfo( int format ) {
	INFUNC( XCAppInterface::GetFormatInfo, this );

	assert( appdic_header );
//	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Find format info: %d", format );
	buffer_format* temp = (buffer_format*)FixOffset( appdic_header->formats.offset );

	// Do a binary search to find the format record
	int Low = 0;
	int High = appdic_header->formats.count - 1;

	while (Low <= High) {
		// Look at the element at the mid point of our limits
		int Index = (Low + High) / 2;
		int value = temp[ Index ].number;

		if (value < format) {
			// Search the lower half
			Low = Index + 1;
		} else if (value > format) {
			// Search the upper half
			High = Index - 1;
		} else {
			// We found it!
			RETURNFUNC( &temp[ Index ] );
		};
	}

	OUTFUNCRET( NULL );
};


buffer_format* XCAppInterface::GetFormatInfo( const char* format_name ) {
	assert( appdic_header );

	buffer_format* temp = (buffer_format*)FixOffset( appdic_header->formats.offset );

	for (int i = 0; i<appdic_header->formats.count; i++) {
		if (strcmp( format_name, temp->real_name) == 0)
			return temp;
		temp++;
	};

	return NULL;
};


buffer_field* XCAppInterface::GetFieldInfo( int field ) {
	INFUNC( XCAppInterface::GetFieldInfo, this );

//	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Find field info: %d", field );

	assert( appdic_header );

	buffer_field* temp = (buffer_field*)FixOffset( appdic_header->fields.offset );

	// Do a binary search to find the format record
	int Low = 0;
	int High = appdic_header->fields.count - 1;

	while (Low <= High) {
		// Look at the element at the mid point of our limits
		int Index = (Low + High) / 2;
		int value = temp[ Index ].number;

		if (value < field) {
			// Search the lower half
			Low = Index + 1;
		} else if (value > field) {
			// Search the upper half
			High = Index - 1;
		} else {
			// We found it!
			RETURNFUNC( &temp[ Index ] );
		};
	}
	OUTFUNCRET( NULL );
};




buffer_field* XCAppInterface::GetFieldInfo( const char* field_name ) {
	INFUNC( XCAppInterface::GetFieldInfo, this );

	assert( appdic_header );
	buffer_field* temp = (buffer_field*)FixOffset( appdic_header->fields.offset );
	for (int i = 0; i<appdic_header->fields.count; i++) {
		if (strcmp( field_name, temp->name) == 0)
			RETURNFUNC( temp );
		temp++;
	};

	OUTFUNCRET( NULL );
};



transaction_defn* XCAppInterface::GetTransaction( int trans_number ) {
	transaction_defn* temp = transaction_defn_list;

	for (int i=0; i<transaction_defn_list_count; i++) {
		if (trans_number == temp->trans_number)
			return temp;
		else
			temp++;
	};

	return NULL;
};


transaction_defn* XCAppInterface::GetTransaction( const char* trans_name ) {
	transaction_defn* temp = transaction_defn_list;

	for (int i=0; i<transaction_defn_list_count; i++) {
		if (strcmp( trans_name, temp->trans_code ) == 0)
			return temp;
		else
			temp++;
	};

	return NULL;
};




appl_defn* XCAppInterface::GetApplDefn( int appl_nbr )
{
	for (int i=0; i<appl_defn_list_count; i++)
	{
		if (appl_defn_list[i].appl_nbr == appl_nbr )
			return &appl_defn_list[i];
	};

	return NULL;
};




int XCAppInterface::GetTraceIndex( trace_categories category ) {
	int ident;

	// TRACE_CATEGORIES
	switch( category ) {
		case TRC_BUFFER:
			ident = 0;
			break;

		case TRC_FIELD:
			ident = 1;
			break;

		case TRC_ERROR:
			ident = 2;
			break;

		case TRC_NOLABEL:
			ident = 3;
			break;

		case TRC_USEREXIT:
			ident = 4;
			break;

		case TRC_DATABASE:
			ident = 5;
			break;

		case TRC_MESSAGING:
			ident = 6;
			break;
		
		case TRC_APPINTERFACE:
			ident = 7;
			break;
	
		case TRC_TRAFFIC:
			ident = 8;
			break;

		case TRC_FUNCTIONS:
			ident = 9;
			break;

		default:
			ident = 10;
	};

	return ident;
};



void XCAppInterface::ToolsTrace(trace_categories category, int level, const char *lpszFormat, ...)
{
	static long counter = 0;

	if (!tracing)
		return;

	int nBuf, ident;

	// TRACE_CATEGORIES
	switch( category ) {
		case TRC_BUFFER:
			ident = 0;
			break;

		case TRC_FIELD:
			ident = 1;
			break;

		case TRC_ERROR:
			ident = 2;
			break;

		case TRC_NOLABEL:
			ident = 3;
			break;

		case TRC_USEREXIT:
			ident = 4;
			break;

		case TRC_DATABASE:
			ident = 5;
			break;

		case TRC_MESSAGING:
			ident = 6;
			break;
		
		case TRC_APPINTERFACE:
			ident = 7;
			break;
	
		case TRC_TRAFFIC:
			ident = 8;
			break;

		case TRC_FUNCTIONS:
			ident = 9;
			break;

		default:
			ident = 10;
	};
		
	// check cutoff value and category filter
	if (level > options.debug_level_cutoff[ ident ] || (options.debug_type_filter & category) != category)
		return;

	char szBuffer[8192];

	va_list args;
	va_start(args, lpszFormat);

	// TRACE_CATEGORIES
	static const char* IDENTIFIERS[ num_tc + 1 ] = {
		"BUFFER: ", "FIELD: ", "ERROR DUMP: ", "", "USEREXIT: ", "DATABASE: ",
		"MESSAGE: ", "APPINTERFACE: ", "TRAFFIC: ", "FUNCTIONS: ", "UNKNOWN: "
	};

	// Write process identifier
	counter++;
	nBuf = sprintf( szBuffer, "[%d.%ld] %ld %s %s",
		XCGetProcessIdentifier(), 
		XCGetThreadIdentifier(),
		counter,
		XCGetSystemTime(),
		IDENTIFIERS[ ident ] );

	nBuf += vsnprintf( &szBuffer[nBuf], sizeof(szBuffer) - nBuf, lpszFormat, args );

	strcat( &szBuffer[ nBuf ], "\n" );

	trace.Lock();

	OUTPUTFUNC( szBuffer );
/*
	if (logging) {
		int retries = 0;
		int result;

		do {
			if (retries) {
				cout << "Retrying send:" << retries << endl;
			};

			result = logging->Send( szBuffer, nBuf );
			retries++;
		} while (retries < 20 && result == -1);
	};
*/

	if ( logfile ) {
		*logfile << szBuffer;
		logfile->flush();
	};
	trace.UnLock();

	va_end(args);
}




XCBuffer* XCAppInterface::CreateBuffer(long FormatNumber, bool load_defaults)
{
	EXT_INFUNC( XCAppInterface::CreateBuffer, this )

	XCBuffer* result = new XCBuffer();
	result->Create( FormatNumber, load_defaults, this );

	EXT_OUTFUNCRET( result )
}





XCBuffer* XCAppInterface::CreateDataBuffer( long FormatNumber, void* new_data ) {
	XCBuffer* result;

	EXT_INFUNC( XCAppInterface::CreateDataBuffer, this )

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "CreateDataBuffer1: %d", FormatNumber );
	result = new XCBuffer();

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "CreateDataBuffer2: %d", FormatNumber );
	result->CreateFromData( FormatNumber, new_data, this );

	EXT_OUTFUNCRET( result )
};



XCBuffer* XCAppInterface::CreateDynamicBuffer(const long *FieldNumbers, bool Load_Defaults, long FormatNumber)
{
	EXT_INFUNC( XCAppInterface::CreateDynamicBuffer, this )

	XCBuffer* result = new XCBuffer();

CHECKMEM;
	result->CreateDynamic( FieldNumbers, Load_Defaults, FormatNumber, this );

	EXT_OUTFUNCRET( result )
}




XCBuffer* XCAppInterface::CreateDynamicBuffer( int count, const buffer_ds* fields, long FormatNumber ) {
	EXT_INFUNC( XCAppInterface::CreateDynamicBuffer, this );
	XCBuffer* result = new XCBuffer();
	result->CreateDynamic( count, fields, FormatNumber, this );
	EXT_OUTFUNCRET( result );
};



XCBuffer* XCAppInterface::CreateDynamicBuffer( buffer_format* fmt)
{
	EXT_INFUNC( XCAppInterface::CreateDynamicBuffer, this );
	XCBuffer* result = new XCBuffer();
	result->CreateDynamic( fmt, this );
	EXT_OUTFUNCRET( result );
};


XCBuffer* XCAppInterface::CreateDynamicField( char* new_name, long new_length, int new_dec_places, int new_type, int new_min_len ) 
{
	EXT_INFUNC( XCAppInterface::CreateDynamicField, this );

	buffer_format* temp_fmt = new buffer_format;
	buffer_ds* temp_ds = new buffer_ds[1];
	buffer_field* temp_fld = new buffer_field;

	// Create new buffer_format record
	temp_fmt->number = 0;
	strcpy( temp_fmt->name, new_name );
	temp_fmt->type = 2;
	temp_fmt->length = new_length;
	temp_fmt->ds_count = 1;
	temp_fmt->ds_offset = (long)((char*)temp_ds - APPDIC_DATA);
	temp_fmt->default_offset = (long)( temp_fld->defaultval - APPDIC_DATA);
	//temp_fmt->dd_offset = buf->mydesc->dd_offset;
	temp_fmt->dd_offset = 0;
	temp_fmt->reserved1 = 0;
	temp_fmt->reserved2 = 0;
	temp_fmt->child_format = 0;

	// Create new data_structure record
	temp_ds->field_offset = (long)((char*)temp_fld - APPDIC_DATA);
	temp_ds->data_offset = 0;
	temp_ds->min_length = new_min_len;
	temp_ds->max_length = new_length;
	temp_ds->alloc_size = new_length;
	temp_ds->dec_places = new_dec_places;
	temp_ds->data_type = new_type;
	temp_ds->flags = 0;
	temp_ds->severity_code = 0;

	// Create new field_desc record
	temp_fld->number = -1;
	strcpy( temp_fld->name, new_name );
	temp_fld->type = new_type;
	temp_fld->length = new_length;
	temp_fld->decimal_places = new_dec_places;
	temp_fld->flags = 0;
	memset( temp_fld->defaultval, 0, new_length );

	XCBuffer* retval = CreateDynamicBuffer( temp_fmt );

	EXT_OUTFUNCRET( retval );
};




struct t_user_info {
	const char* username;
};


struct ue_info {
	// General
	long		transaction_number;
	t_user_info	user_info;

	// Storage
	char*		process_storage;

	// Return information
	char		message[256];
	char		field_name[24];

	void*		buffer[10];
	int			buffer_number[10];

	// Available for Protea Project interfacing
	XCBuffer* buffer_ptr[10];
	XCTransactionVars* vars;
};


int XCAppInterface::TriggerUserExit(long ue_num, XCBufferList *buffer_list, XCTransactionVars* vars)
{
	typedef int (*XCUserExitCall)(void*);

	EXT_INFUNC( XCAppInterface::TriggerUserExit, this );

	// Do we have user-exit functionality available?  // hard-code to "true" for now
	if ( true ) {
		ToolsTrace( TRC_USEREXIT, TLV_HIGH, "Trigger: %d", ue_num );

		//
		// Determine if the user exit is enabled
		// 
		user_exit_defn* defns = (user_exit_defn*)FixOffset( appdic_header->user_exits.offset );
		int count = 0;
		while (count < appdic_header->user_exits.count) {
			if (defns->number == ue_num)
				break;
			defns++;
			count++;
		};

		// Did we find the user exit?
		if (count == appdic_header->user_exits.count)
			RAISETOOLSERROR_1PARAM( ERR_NO_UE_NUMBER, ue_num );

		// Check to see if the user-exit is active
		if (defns->active) {
			// Ensure that the user exit library is loaded
			if (!uelib)
				RAISETOOLSERROR( ERR_NO_UE_LIB );

			char temp[ 20 ];
			XCUserExitCall uecall;

			sprintf( temp, "UE%ld", ue_num );
			try {
				uecall = (XCUserExitCall)uelib->GetLibAddress( temp );
			} catch( ... ) {
				uecall = NULL;
			};

			if (!uecall) {
				ToolsTrace( TRC_USEREXIT, TLV_HIGH, "UE function '%s' not found!", temp );
				RAISETOOLSERROR_1PARAM( ERR_NO_UE_FUNCTION, temp );
			};

			// Create user exit info structure
			int result, i;
			ue_info* info = new ue_info;
			memset( info, 0, sizeof( ue_info ) );

			// Create a parameter buffer
			if (buffer_list) {
				// Fill in pointers to the buffers
				for (i = 0; i<buffer_list->buffercount; i++) {
					XCBuffer* buffer = buffer_list->GetBuffer(i);
					info->buffer[i] = buffer->GetData();
					info->buffer_number[i] = buffer->GetFormatNumber();
					info->buffer_ptr[i] = buffer;
				};
			};

/*
			// Create a parameter buffer
			ue_info* info;
			if (buffer_list) {
				info = (ue_info*)new char[ sizeof(ue_info) + sizeof( long ) * buffer_list->buffercount ];

				// Fill in pointers to the buffers
				XCBuffer** next_loc = (XCBuffer**)((char*)info + sizeof( ue_info ));
				for (i = 0; i<buffer_list->buffercount; i++) {
					*next_loc = buffer_list->GetBuffer( i );
					next_loc++;
				};
			} else
				info = new ue_info;
*/

			info->process_storage = process_storage;
			if (vars) {
				info->vars = vars;
				if (vars->credentials)
					info->user_info.username = vars->credentials->GetUserName();
				if (vars->result)
                    info->transaction_number = vars->result->GetTransNumber();
			};

			ToolsTrace( TRC_USEREXIT, TLV_HIGH, "Execute UE call" );
			result = uecall( info );

			//
			// Description of result variable:
			//		0	= OK result
			//		1	= Warning
			//		-1	= Error
			//

			// Did we have an error case?
			if (result != 0) {
				// Determine the error field
				int fld_nbr = 0;
				if (info->field_name && *info->field_name) {
					buffer_field* fld = GetFieldInfo( info->field_name );
					fld_nbr = fld->number;
				};

				try {
					if (vars && vars->Errors) {
						// If we are supplied an error class, use it
						error_severity severity = (result == -1) ? SEV_ERROR : SEV_WARNING;
						vars->Errors->Add( ERR_USER_EXIT_MESSAGE, severity, fld_nbr, 1, info->message );
					} else {
						// Raise the error
						RAISEFIELDERROR_1PARAM( ERR_USER_EXIT_MESSAGE, fld_nbr, info->message );
					};
				} catch( ... ) {
					delete info;
					throw;
				};
			};
			delete info;

			EXT_RETURNFUNC( result );
		};
	};

	EXT_OUTFUNCRET( 0 );
}




XCDBHandler* XCAppInterface::GetDatabase()
{
	return db;
}




int XCAppInterface::StartApp( PORT_HANDLE in, long type, init_message& init ) {
	INFUNC( XCAppInterface::StartApp, this )

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Incoming port handle: %ld", in );

	// Delete all of our open connections if they exist (inherited from the parent)
	ClearPorts( true );

	parent = new XCMsgPort( NULL, this );

	APP_LOOPBACK_INIT;

	parent->SetTransport( loopback );

	// Attempt to read information from the master
	int test = parent->transport->Recv( &init, sizeof( init_message ) );
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "--returned : %d", test );

	APP_LOOPBACK_OUTPUT;

	parent->porttype = pt_misc;
	parent->ready = true;

	// Close pipes that we will not be using
	XCPipeTransport closeme( this, init.close_me[0], init.close_me[1] );

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Tools version: %d.%d", init.major, init.minor );

	// Set the parent process id
	parentpid = init.parentpid;

	// Set our destination type (gcs, app, interface, etc)
	mType = type;

	// Set our debug information
	XCAppOptions myopts;
	GetOptions( myopts );
	myopts.debug_type_filter = init.startup_parms.debug_type_filter;
//	printf( "Debug flags: %d\n", myopts.debug_type_filter );
	for (int i=0; i<num_tc; i++) {
		myopts.debug_level_cutoff[ i ] = init.startup_parms.debug_level_cutoff[ i ];
//		printf( "\tDebug level: %d\n", myopts.debug_level_cutoff[ i ] );
	};
	SetOptions( myopts );

	// Set the authentication method
	auth_method = (AuthenticateMethod)init.startup_parms.authentication_method;
	int offset = 0;
	int len = init.startup_parms.authentication_parm_len;
	if (len) {
		auth_parameter = new char[ len+1 ];
		memcpy( auth_parameter, init.startup_parms.data, len );
		auth_parameter[ len ] = 0;
		offset += len;
	} else
		auth_parameter = NULL;

	// Set the smtp host
	len = init.startup_parms.smtp_len;
	if (len) {
		smtp = new char[ len + 1 ];
		memcpy( smtp, init.startup_parms.data + offset, len );
		smtp[ len ] = 0;
		offset += len;
	} else {
		smtp = NULL;
	};

	// Set the printer
	len = init.startup_parms.printer_len;
	if (len) {
		printer = new char[ len + 1 ];
		memcpy( printer, init.startup_parms.data + offset, len );
		printer[ len ] = 0;
		offset += len;
	} else
		printer = NULL;

	// Set the email address
	len = init.startup_parms.admin_email_len;
	if (len) {
		admin_email = new char[ len + 1 ];
		memcpy( admin_email, init.startup_parms.data + offset, len );
		admin_email[ len ] = 0;
		offset += len;
	} else
		admin_email = NULL;

	// Set the fop location
	len = init.startup_parms.fop_location_len;
	if (len) {
		fop_location = new char[ len + 1 ];
		memcpy( fop_location, init.startup_parms.data + offset, len );
		fop_location[ len ] = 0;
		offset += len;
	} else
		fop_location = NULL;

	// Set the number of worker threads
	worker_threads = init.startup_parms.worker_threads;

	OUTFUNCRET( 0 )
};



void XCAppInterface::ClearPorts( bool force )
{
	// Delete all of our open connections
	if (parent) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Delete parent" );
		if (force)
			parent->killme = true;
		parent->Release();
		parent = NULL;
	};

	childlistlock.Lock();
	if (childlist) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Delete child" );
		XCMsgPortPtr tempport = childlist;
		while (tempport) {
			XCMsgPortPtr temp2 = tempport->next;
			if (force)
				tempport->killme = true;
			tempport->Release();
			tempport = temp2;
		};

		childlist = NULL;
	};
	childlistlock.UnLock();

	if (sockin) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Delete listener" );
		if (force)
			sockin->killme = true;
		sockin->Release();
		sockin = NULL;
	};
};





XCMsgPortPtr XCAppInterface::RemoveChild( XCMsgPortPtr* childlist, XCMsgPortPtr toremove, XCMsgPortPtr prev ) {
	XCMsgPortPtr next = toremove->next;


	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Removing %d", toremove->transport->GetInHandle() );

	// Remove object from list of children
	if (prev)
		prev->next = next;
	else {
		// Is this the first element
		if (*childlist == toremove)
			*childlist = next;
		else {
			// Otherwise look for the element before the one we want to delete
			XCMsgPort* temp = *childlist;
			while (temp->next != toremove && temp->next)
				temp = temp->next;

			// Did we find the node?
			if (temp->next)
				temp->next = next;
		}
	};

	delete toremove;
	return next;
};


//
// Callback functions
//
void XCAppInterface::OnDroppedConnection( XCMsgPortPtr tempport ) {
};

void XCAppInterface::DroppedProcess( pid_t child_pid ) {
	OnDroppedProcess( child_pid );
};

void XCAppInterface::OnDroppedProcess( pid_t child_pid ) {
	if (process)
		cout << process << " - ";
	cout << "Caught the exit of child process: " << child_pid << endl;
	XCDeleteProcess( process_list, child_pid );
};





void XCAppInterface::BroadcastMessage( XCMsgPortPtr src, XCToolsMessage* value ) {
	INFUNC( XCAppInterface::BroadcastMessage, this );

	DebugMessageTraffic( "Broadcast message from ", src );

	APP_LOCK_CHILDLIST;

	XCMsgPortPtr tempport = childlist;
	while (tempport) {
		if (tempport != src) {
			SendMessage( src, tempport, value );
			value->RemoveRouteInfo();
		};
		tempport = tempport->next;
	};

	APP_UNLOCK_CHILDLIST;

	OUTFUNC();
};


void XCAppInterface::ForwardMessage( XCMsgPort* port, XCToolsMessage* msg, XCMsgPort* dest_port ) {
	bool towards_gcs;
	XCMsgPortPtr dest;

	INFUNC( XCAppInterface::ForwardMessage, this );

	base_message* base = msg->GetHeaderPtr();
	bool query_message = base->miscflag & misc_query;

	if (base->sdflag & dest_gcs) {
		towards_gcs = true;
	} else if ((base->sdflag & dest_int) && parent) {
		// Special case for route from GCS to interface
		towards_gcs = true;
	} else
		towards_gcs = false;

	if (!query_message) {
		// Pop the last routing info (it's from the previous sender and we don't need it)
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Pop last route" );
		msg->RemoveRouteInfo();
	};

	if (towards_gcs) {
		// If message is heading towards gcs, always forward to parent
		dest = parent;
	} else {
		if (!query_message) {
			// get route id
			route_info* route = msg->PeekLastRoute();
			assert( route->src );

	//		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Locate child: %d, %ld", route->src, XCGetProcessID() );
//			printf( "%s Locate child: %d\n", process, route->src );
			dest = childlist;
			while ( dest && dest->transport->GetInHandle() != (PORT_HANDLE)route->src ) {
				ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "No: %d", dest->transport->GetInHandle() );
				dest = dest->next;
			};
		} else {
			// If we are in a query, push down to the next level
			dest = dest_port;
			assert( dest );

			// Add routing information
			msg->AddRouteInfo( port, dest );
		};
	};

	if (!dest) {
//		printf( "%s UNABLE TO LOCATE DESTINATION!!!\n", process );
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Unable to locate destination" );
		EXITFUNC();
	};

	DebugMessageTraffic( "Forwarding message to", dest );

	// Check to see if we need to send in two chunks
	base_message* header = msg->GetHeaderPtr();
	long size = msg->GetDataFullSize();
	if (!msg->UsesCompression())
		dest->transport->Send( header, size );
	else {
		dest->transport->Send( header, BASE_MESSAGE_SIZE );
		dest->transport->Send( msg->GetCompressPtr(), header->data_size );
	};

	OUTFUNC();
};


void XCAppInterface::SendMessage( XCMsgPort* src, XCMsgPort* dest, XCToolsMessage* value )
{
	INFUNC( XCAppInterface::SendMessage, this )

	assert( dest );
	assert( value );

	msg_sent++;

	value->AddRouteInfo( src, dest );

	// Calculate size of message
	base_message* header = value->GetHeaderPtr();
	long size = value->GetDataFullSize();

	DebugMessageTraffic( "Send message to", dest );
	value->Dump();

	// Attempt to compress the message
//	value->Compress();
//	value->Dump();

	// Check to see if we need to send in two chunks
	if (!value->UsesCompression()) {
		dest->transport->Send( header, size );
	} else {
		dest->transport->Send( header, BASE_MESSAGE_SIZE );
		dest->transport->Send( value->GetCompressPtr(), header->data_size );
	};

	OUTFUNC()	
};

void XCAppInterface::AdminMsg( const long errnum, long field, long parmcount, ... )
{
	EXT_INFUNC( XCAppInterface::AdminMsg, this )

	// Set the administration messages to be in language 0
	long language = XCGetLanguage();
	XCSetLanguage( 0 );

	try {
		XCToolsError* error = new XCToolsError( errnum, field, 0 );
		if (parmcount) {
			// Read our arguments
			va_list parmlist;
			va_start( parmlist, parmcount );
			for (int i=0; i<parmcount; i++)
				error->AddParm( va_arg( parmlist, void* ) );
			va_end( parmlist );
		};

		// Formulate the description
		error->MakeDescr( this );

		XCToolsMessage* temp = new XCToolsMessage( this );
		temp->CreateMsg( ADMIN_MESSAGE_SIZE + strlen( error->GetDescription() ) );
		temp->SetHeader( msg_admin, src_app, dest_gcs, false, false );
		temp->AddRouteInfo( NULL, parent );
		admin_message* detail = (admin_message*) temp->GetDataPtr();
		detail->err_num = errnum;
		detail->field_num = field;
		detail->src_pid = XCGetProcessIdentifier();
		strcpy( detail->err_message, error->GetDescription() );

		temp->Dump();
		SendMessage( msg_src, parent, temp );
	} catch( ... ) {
		XCSetLanguage( language );
	};
	XCSetLanguage( language );

	EXT_OUTFUNC()
};


XCMsgPortPtr XCAppInterface::CreateChild( char* app, long pcount, char** parms, bool usepipe, app_start_message* tempappl )
{
	XCMsgPortPtr new_port = NULL;
	INFUNC( XCAppInterface::CreateChild, this )

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Making new process : %s", app );

	process_info* result = XCMakeChild( this, app, process_list, pcount, parms, usepipe, tempappl );

	if ( usepipe )
	{
		new_port = new XCMsgPort( result, this );
		new_port->SetTransport( result->server );

		childlistlock.Lock();
		if (childlist) {
			XCMsgPortPtr tempport = childlist;
			while (tempport->next)
				tempport = tempport->next;
			tempport->next = new_port;
		} else
			childlist = new_port;
		childlistlock.UnLock();

		APP_CREATE_CHILD_LISTEN;

	};

	OUTFUNCRET( new_port )
};


XCMsgPortPtr XCAppInterface::Connect( char* host, int port )
{
	XCMsgPortPtr new_port;

	INFUNC( XCAppInterface::Connect, this )

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Connect %s %d", host, port );

	XCSocketTransport* sockout = new XCSocketTransport( this );

	new_port = new XCMsgPort( NULL, this );
	new_port->SetTransport( sockout );
	sockout->Create( AF_INET, SOCK_STREAM, 0 );
	sockout->Connect( host, port );
	
	childlistlock.Lock();
	XCMsgPortPtr tempport = childlist;
	if (tempport)
	{
		while ( tempport->next != NULL)
			tempport = tempport->next;
		tempport->next = new_port;
	}
	else
		childlist = new_port;
	childlistlock.UnLock();

	sockout->SetBlockingMode( false );
	
//	int size = sizeof( server_address );
//	getsockname( (SOCKET)new_port->pins, (struct sockaddr*)&server_address, &size );
//	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "######## Connect to %d : %d", port, htons(server_address.sin_port) );

	APP_CONNECT_LISTEN;

	OUTFUNCRET( new_port )
};


void XCAppInterface::QuitLoop()
{
	INFUNC( XCAppInterface::QuitLoop, this )

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "XCAppInterface Message Loop stopping now" );
	CancelMessageLoop();

	OUTFUNC()
};



struct ThreadInfo {
	XCAppInterface* app;
	XCMsgPortPtr parent;
	XCMsgPortPtr* childlist;
	XCMsgPortPtr sockin;
	route_loc dest;
};



THREAD_PROCEDURE XCAppInterface::MessageLoopThread( void* parm ) {
	ThreadInfo* ti = (ThreadInfo*)parm;

	printf( "Start thread\n" );
	try {
		ti->app->ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "-- STARTING MESSAGE LOOP THREAD --" );
		while (!ti->app->GetAbortFlag()) {
			ti->app->ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "-- %ld -- call GetMessage( %ld, %ld, %ld )", ti->app, ti->parent, ti->childlist, ti->sockin );
			ti->app->GetMessage( ti->parent, ti->childlist, ti->sockin );
			ti->app->ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "-- Leave GetMessage" );
		};
		ti->app->ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "-- ENDING MESSAGE LOOP THREAD --" );
	} catch( XCToolsError& error ) {
		XCOutputCallStack( ti->app, error.GetErrorNumber(),error.GetDescription() );
		printf( "Exit thread in error\n" );
		return 0;
	};
	printf( "Exit thread\n" );
	return 0;
};



THREAD_PROCEDURE XCAppInterface::StatedTxnCleanupThread( void* parm ) {
	ThreadInfo* ti = (ThreadInfo*)parm;
	assert( ti->app );

	int sleep_time = 5 * 60;

	XC_WAIT_RESULT result;
	do {
		result = ti->app->abort.Wait( sleep_time );
		if (result == XCWAIT_TIMEOUT) {
			printf( "[%s] Cleanup\n", ti->app->process );
			ti->app->StatedTxnList->Cleanup();
		};
	} while (result == XCWAIT_TIMEOUT);

	return 0;
};


THREAD_PROCEDURE XCAppInterface::MessageThread( void* parm ) {
	ThreadInfo* ti = (ThreadInfo*)parm;

	assert( ti->app );

	ti->app->StartMessageLoop( ti->dest );

	int thr_count = ti->app->worker_threads;
	THREAD_HANDLE* handles = new THREAD_HANDLE[ thr_count+1 ];

	// Create the transaction cleanup thread
	handles[thr_count] = XCCreateThread( StatedTxnCleanupThread, parm );

	APP_MESSAGE_THREAD;

	delete [] handles;

	// clean up children
	ti->app->CleanupPorts();
	delete ti;

	return 0;
};



void XCAppInterface::CleanupPorts() {
	childlistlock.Lock();
	XCMsgPortPtr tempport = childlist;
	while (tempport)
	{
		if ( tempport->ready )
			tempport->transport->Close();
		tempport = tempport->next;
	}
	childlistlock.UnLock();

	// clean up connection to parent
	if ( parent )
		parent->transport->Close();

	// stop listener
	if ( sockin )
		sockin->transport->Close();

	ports.Close();
};


THREAD_HANDLE XCAppInterface::MessageLoop( route_loc dest, bool thread ) 
{
	INFUNC( XCAppInterface::MessageLoop, this )

	THREAD_HANDLE result = 0;

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "-- %ld -- Start Message Loop", this );

	// thread here
	ThreadInfo* ti = new ThreadInfo;
	ti->app = this;
	ti->childlist = &childlist;
	ti->parent = parent;
	ti->sockin = sockin;
	ti->dest = dest;

	if (thread) {
		result = XCCreateThread( MessageThread, (void*)ti );
	} else
		MessageThread( (void*)ti );

	OUTFUNCRET( result )
};



bool XCAppInterface::GetAbortFlag() {
	return abort.GetState();
};




int XCAppInterface::MainFunc( int socklisten )
{
	INFUNC( XCAppInterface::MainFunc, this )

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "MainFunc: Awaiting socket requests" );

	sockin = new XCMsgPort( NULL, this );
	sockin->porttype = pt_listen;
	sockin->ready = true;

	XCSocketTransport* connect = new XCSocketTransport( this, sockin );
	sockin->transport = connect;

	connect->Create( AF_INET, SOCK_STREAM, 0 );
	connect->SetBlockingMode( false );
	connect->Bind( socklisten );

	// Listen for new connections
	connect->Listen();

	OUTFUNCRET( connect->GetPort() )
};





bool XCAppInterface::HandleOptionMessage( XCMsgPort* port, XCToolsMessage* msg ) {
	INFUNC( XCAppInterface::HandleOptionMessage, this );

	option_message* opmsg;
	opmsg = (option_message*)msg->GetDataPtr();
	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Received Option Message for Host: %s, PID: %ld", opmsg->hostname, opmsg->dest_pid );
	
	if ( XCGetProcessIdentifier() == opmsg->dest_pid ) {
		char localhost[ MAX_PATH ];
		XCGetHostName( localhost, MAX_PATH );
		if ( strcmp( localhost, opmsg->hostname ) == 0 ) {
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "*** Modifiying AppInterface Options ***" );
			double filter = pow( 2.0, (int)opmsg->debug_type_filter );
			long lfilter = (long)filter;
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Set debug_type_filter = %ld", lfilter );

			if (opmsg->enabled)
				options.debug_type_filter |= lfilter;
			else
				options.debug_type_filter &= ~lfilter;

			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Set debug_level_cutoff[ %d ] = %ld (on/off = %d,%d)", opmsg->debug_type_filter, opmsg->debug_level_cutoff, (options.debug_type_filter & lfilter) > 0 ? 1 : 0, opmsg->enabled );
			options.debug_level_cutoff[ opmsg->debug_type_filter ] = opmsg->debug_level_cutoff;

			// Send an update of our debug status to GCS
			SendStatus( src_app, parent );
		};
	};

	OUTFUNCRET( true );
};


void XCAppInterface::HandleSecurityChange( XCToolsMessage* msg ) {
	// Now fill in the appropriate variables
	disp_message* dmsg = (disp_message*)msg->GetDataPtr();
	security_update_message* update = (security_update_message*)&dmsg->data;
	security_info_manager info;
	info.FromMessageType( update );

	// Do some initial lookup of information
	XCUserInformation* user = NULL;
	if (info.user_name)
		user = users.Find( info.user_name );

	XCGroupInformation* group = NULL;
	if (info.group_name)
		group = groups.Find( info.group_name );

	switch( update->mode ) {
		case SECURITY_ADD_USER:
			users.Add( info.user_name, info.user_password, info.description );
			break;
		case SECURITY_DEL_USER:
			if (user)
				users.Remove( user );
			break;
		case SECURITY_CHG_USER:
			break;
		case SECURITY_ADD_GROUP:
			groups.Add( info.group_name, info.description );
			break;
		case SECURITY_DEL_GROUP:
			if (group)
				groups.Remove( group );
			break;
		case SECURITY_ADD_USER_TO_GROUP:
			if (user && group)
				group->AddUser( user );
			break;
		case SECURITY_DEL_USER_FROM_GROUP:
			if (user && group)
				group->RemoveUser( user );
			break;
	};
};



bool XCAppInterface::HandleDispatchMessage( XCMsgPort* port, XCToolsMessage* msg ) {
	INFUNC( XCAppInterface::HandleDispatchMessage, this );

	disp_message* dmsg;
	disp_message* disp_info;
	XCToolsMessage* reply;
	dmsg = (disp_message*)msg->GetDataPtr();
	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Dispatch Message Opcode : %ld" , dmsg->opcode );

	switch( dmsg->opcode ) 
	{
		case disp_shutdown:
			// disp_shutdown_complete:
			reply = new XCToolsMessage( this );
			reply->CreateMsg( DISP_MESSAGE_SIZE );
			reply->SetHeader( msg_dispatch, src_app, dest_gcs, true, false );
			disp_info = (disp_message*)reply->GetDataPtr();
			disp_info->opcode = disp_shutdown_confirm;
			if (parent)
				SendMessage( msg_src, parent, reply );
			reply->DeleteMsg();
			delete reply;
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "XCAppInterface::MessageLoop Stopping Now" );
			QuitLoop();
			RETURNFUNC( false );
			break;

		case disp_pushdown_query:
			// Request for status
			SendStatus( src_app, parent );
			break;

		case disp_contact_ui: 
			if (mType == dest_app) {
				// Connect directly to host
				contact_ui_message* temp_msg = (contact_ui_message*)dmsg->data;
				XCMsgPortPtr outport;

				ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Received UI contact message: %ld %s", temp_msg->port, temp_msg->host );
				outport = Connect( temp_msg->host, temp_msg->port );
				msg->ClearRoutes();
				SendMessage( port, outport, msg );
				break;
			} else {
				XCMsgPortPtr outport = NULL;
				XCMsgPortPtr tempport = childlist;
				while (tempport)
				{
					if (tempport->porttype == pt_gcs)
						outport = tempport;
					tempport = tempport->next;
				}
				if (!outport)
					RAISETOOLSERROR( -1 );
				
				// remove outport from the childlist
				if (outport == childlist)
					childlist = outport->next;
				else
				{
					tempport = childlist;
					while (tempport)
						if (outport == tempport->next)
							tempport->next = outport->next;
				};
				OnConnect( port );
				outport->Release();
//						CancelMessageLoop();
			};
			break;

		case disp_security_change:
			HandleSecurityChange( msg );
			break;

		// Used to abort an inprocess transaction fragment (concurrency limits, etc)
		case disp_abort_txn:
			DelTxnFragment( *(long*)dmsg->data );
			break;

		default:
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Invalid Dispatch Message" );
			break;
	};

	OUTFUNCRET( true );
};




bool XCAppInterface::HandleTransactionMessage( XCMsgPort* port, XCToolsMessage* msg ) {
	INFUNC( XCAppInterface::HandleTransactionMessage, this );

	assert( appdic_header );

	XCTransactionInfo* txn = NULL;
	XCTransactionInfo* txnreply = NULL;
	XCToolsMessage* reply;

	base_message* base = msg->GetHeaderPtr();
	trans_query_base* trans = (trans_query_base*)msg->GetDataPtr();
	trans_message* trans_info = NULL;

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Transaction message: id=%d, order=%d, type=%d", trans->transid, trans->order, trans->data_type );
	try {
		if (base->sdflag & current_loc) {
			// find txn in TxnList
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Find Transaction %ld", trans->transid );

			txn = AddTxn( msg, port );
			if (txn) {
				char* temperr = NULL;

				// Callback function for transactions
				ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Transaction complete, id=%d", txn->GetTransID() );

				// Check Security only at app
				bool app_dest = (base->sdflag & dest_app);
				bool secure = true;

				// If this message is destined for an application, we need to
				// verify username/password and authorize all actions
				try {
					if ( app_dest ) {
						if (txn->GetJob()) {
							txn->credentials = new XCCredentials( txn->GetUser(), this );
						} else {
							char password[ 256 ];
							txn->GetPass( password, txn->GetTransID() );
							try {
								txn->credentials = Authenticate( txn->GetUser(), password );
							} catch( ... ) {
								memset( password, 0, 256 );
								throw;
							};
							memset( password, 0, 256 );
						};
						secure = (txn->credentials != NULL);
					};


					// Did we pass authentication?
					if (secure) {
						ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "SUBMIT TRANSACTION" );

						// Special case for query transactions - never part of a supertransaction
						if (txn->IsQuery()) {
							// Send transaction to the host application
							txn->SetInputInfo( port );

							// Are we allowed to access this application?
							if (txn->credentials)
								txn->credentials->AuthorizeApplication( GetAppNumber() );

							txnreply = OnQuery( txn );
						} else {
							// Do we have a create action!
							if (txn->GetAction() == 1) {
								ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "CREATE SUPER START" );
								txn->SetSuperTrans( AddSuper() );
								ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "CREATE SUPER END" );
								if (txn->GetTotalCount() == 0) {
									txnreply = new XCTransactionInfo( this, port );
									txnreply->SetSuccess( true );
									txnreply->SetTransID( txn->GetTransID() );
									txnreply->SetTransNumber( txn->GetTransNumber() );
									txnreply->SetSuperID( txn->GetSuperID() );
								};
							};

							// Was this a super-transaction request?
							if (txn->GetTotalCount() > 0 || txn->GetAction() == 0) {
								// Send transaction to the host application
								txn->SetInputInfo( port );

								if (txn->GetTransNumber() > 0 || !app_dest) {
									if (txn->credentials)
										txn->credentials->AuthorizeTransaction( txn );

									txnreply = OnTransaction( txn );
								} else {
									// Are we allowed to access this application?
									if (txn->credentials)
										txn->credentials->AuthorizeApplication( GetAppNumber() );
									txnreply = SystemTransaction( txn );
								};
							};

							// Do we have a close action?
							if (txn->GetAction() >= 2) {
								ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Close: %d", txn->GetAction() );

								// Issue a commit/rollback
								XCTxn* supertxn = txn->GetSuperTrans();
								if (supertxn) {
									ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "EndTxn: %d", txn->GetAction() == 2 );
									supertxn->EndTxn( txn->GetAction() == 2 );
									txn->SetSuperTrans( NULL );
								} else
									RAISETOOLSERROR( 1 );

								// Was this a stand-alone transaction?  If so, respond
								if (txn->GetTotalCount() == 0) {
									txnreply = new XCTransactionInfo( this, port );
									txnreply->SetSuccess( true );
									txnreply->SetTransID( txn->GetTransID() );
									txnreply->SetTransNumber( txn->GetTransNumber() );
								};
							};

							ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "END SUBMIT TRANS" );
						};
					} else {
						ToolsTrace( TRC_APPINTERFACE, TLV_ALWAYS, "User : %s failed security check.", txn->GetUser() );
						txnreply = new XCTransactionInfo( this, port );

						int err_number;
						if (txn->GetUser()) {
							err_number = ERR_USER_FAIL_SECURITY;
							txnreply->SetUser( txn->GetUser() );
						} else
							err_number = ERR_USER_ID_NOT_SENT;
						XCToolsError txnerr( err_number, 0, this, 0, 0, 1, txn->GetUser() );
							
						txnreply->SetSuccess( false );
						txnreply->SetTransID( txn->GetTransID() );
						txnreply->SetTransNumber( txn->GetTransNumber() );
						txnreply->SetSession( txn->GetSession() );
						TransResponseMsg( txnreply, 0, txnerr.GetDescription(), 0, 0, SEV_CRITICAL );
					};
				} catch( XCToolsError& err ) {
					// Special case for SAI, returns error 0
					if (err.GetErrorNumber() != 0) {
						// If we have an unhandled exception, return the error message
						const char* errstr = err.GetDescription();
						txnreply = new XCTransactionInfo( this, port );
						txnreply->SetSuccess( false );
						txnreply->SetTransID( txn->GetTransID() );
						txnreply->SetTransNumber( txn->GetTransNumber() );
						txnreply->SetPhase( txn->GetPhase() );
						txnreply->SetUser( txn->GetUser() );
						txnreply->SetSession( txn->GetSession() );
						TransResponseMsg( txnreply, err.GetErrorNumber(), errstr, 0, err.GetField(), SEV_CRITICAL );
					};
				};

				// Check to see if transaction sent a response message
				if (txnreply) {
					ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Reply to transaction" );
					SendTransaction( txnreply, port, txn->GetHeader() );

					// clean up...
					txnreply->Release();
					if (temperr)
						delete[] temperr;
				} else {
					ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "Warning: Transaction did not return results" );
				};

				// Release transaction
				DelTxn( txn );
			};
		} else {
			ForwardMessage( port, msg );
			return true;
		};
	} catch( XCToolsError& err ) {
		const char* errstr = err.GetDescription();
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "error message: %s", errstr );

		int size = sizeof( trans_error_message ) + strlen( errstr );

		// Formulate actual reply message
		reply = new XCToolsMessage( this );
		reply->CreateMsg( TRANS_MESSAGE_SIZE + size );
		reply->SetReply( *msg );
		reply->SetHeader( msg_trans_response );

		trans_info = (trans_message*)reply->GetDataPtr();
		memcpy( trans_info, msg, TRANS_MESSAGE_SIZE );
		trans_info->data_type = TRANS_MESSAGE_TYPE;
		trans_info->order = 0;
		trans_info->format = 0;

		// Prepare message information
		trans_error_message *temp = (trans_error_message*)trans_info->data;
		temp->number = err.GetErrorNumber();
		temp->severity = SEV_CRITICAL;
		temp->fieldnumber = 0;
		strcpy( temp->text, errstr );

		((char*)temp)[size-1] = 0;

		SendMessage( msg_src, port, reply );
		reply->DeleteMsg();
		delete reply;
	};

	OUTFUNCRET( true );
};



//
// Callback Functions
//
bool XCAppInterface::GotMessage( XCMsgPort* port, XCToolsMessage* msg )
{
	INFUNC( XCAppInterface::GotMessage, this )

	bool retval = true;
	base_message* base = msg->GetHeaderPtr();

	msg_recv++;

	DebugMessageTraffic( "Got message from", port );
	msg->Dump();

	// Act on message

	// Do we need to broadcast to all children?
	if (base->miscflag & misc_broadcast)
		BroadcastMessage( port, msg );

	switch( base->type ) {
	
		case msg_options:
			retval = HandleOptionMessage( port, msg );
			break;

		case msg_dispatch: 
			retval = HandleDispatchMessage( port, msg );
			break;

		case msg_user:
			OnUserMessage( (char*)msg->GetDataPtr() );
			break;

		case msg_trans:
		case msg_sql_query:
			retval = HandleTransactionMessage( port, msg );
			break;

		default:	
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Invalid Message Type : %ld", base->type );
	};

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "XCAppInterface::GotMessage complete" );

	OUTFUNCRET( retval );
};





XCTransactionInfo* XCAppInterface::OnTransaction( XCTransactionInfo* )
{
	INFUNC( XCAppInterface::OnTransaction, this );

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "ERROR : XCAppInterface::OnTransaction called" );
	RAISETOOLSERROR( -1 );

	OUTFUNCRET( NULL );
};



// Handles system level transactions like login/logout
XCTransactionInfo* XCAppInterface::SystemTransaction( XCTransactionInfo* txn ) {
	INFUNC( XCAppInterface::SystemTransaction, this );

	XCTransactionInfo* reply = NULL;

	switch( txn->GetTransNumber() ) {
		case -1:	// Login
			reply = HandleLogin( txn );
			break;

		case -2:	// Logout
			reply = HandleLogout( txn );
			break;

		case -3:	// Close tranasction
			reply = CloseTransaction( txn );
			break;

		default:
			RAISETOOLSERROR( ERR_BAD_TXN );
	};

	OUTFUNCRET( reply );
};



XCTransactionInfo* XCAppInterface::HandleLogin( XCTransactionInfo* transaction ) {
	XCTransactionVars txnvars( this, transaction, false );
	printf( "Received login transaction\n" );
	txnvars.result->SetSuccess( true );
	txnvars.Errors->Finish();
	return txnvars.ReturnResult();
};

XCTransactionInfo* XCAppInterface::HandleLogout( XCTransactionInfo* transaction ) {
	XCTransactionVars txnvars( this, transaction, false );
	printf( "Received logout transaction\n" );
	txnvars.result->SetSuccess( true );
	txnvars.Errors->Finish();	
	return txnvars.ReturnResult();
};

XCTransactionInfo* XCAppInterface::CloseTransaction( XCTransactionInfo* transaction ) {
	// This is the transaction ID to kill
	long id = transaction->GetSuperID();

	XCTransactionVars txnvars( this, transaction, false );
	printf( "Cancel transaction: %d\n", id );

	// Cleanup any stated transactions with this id
	StatedTxnList->Remove( id );

	// Delete any transaction fragments that our outstanding
	DelTxnFragment( id );

	// Return a success message
	txnvars.result->SetSuccess( true );
	txnvars.Errors->Finish();
	return txnvars.ReturnResult();
};



XCTransactionInfo* XCAppInterface::OnQuery( XCTransactionInfo* txn )
{
	XCTransactionInfo* txnreply = NULL;

	INFUNC( XCAppInterface::OnTransaction, this );

	XCBuffer* buffer = NULL;
	XCBuffer* full_buffer = NULL;
	XCBuffer* count_buffer = NULL;
	XCDBStatement* stmt = NULL;
	XCTxn* dbtxn = NULL;

	XCSQLGen query( this );

	long recordstart = txn->GetStartRecord();
	long recordcount = txn->GetRecordCount();
	long wherecount = 0;
	long ordercount = 0;
	XCCompare* comps = NULL;
	XCOrder* orders = NULL;
	long* order_list = NULL;
	XCField** where_list = NULL;
	long* field_list = NULL;

	// Read any selection parameters
	XCMessageList* temp = txn->GetFirstMessage();
	while (temp) {
		XCToolsMessage* msg = temp->msg;
		if (msg->GetHeaderPtr()->type == msg_sql_query) {
			sql_query_result_message* message = (sql_query_result_message*)msg->GetDataPtr();
			if (message->data_type == QUERY_FIELD_INFO) {
				char* data = message->data;

				wherecount = message->wherecount;

				field_list = new long[ message->fieldcount+1 ];
				orders = new XCOrder[ message->fieldcount ];
				order_list = new long[ message->fieldcount+1 ];

				if (wherecount) {
					comps = new XCCompare[ wherecount+1 ];
					where_list = new XCField*[ wherecount+1 ];
				};

				// Create a list of requested fields for the dynamic buffer creation
//				printf( "Number of fields: %d\n", message->fieldcount );
				for (int i=0; i<message->fieldcount; i++) {
					sql_query_field* fld = (sql_query_field*)data;
					data += sizeof( sql_query_field ) + fld->valuesize;

					field_list[ i ] = fld->number;
				};
				field_list[ message->fieldcount ] = 0;

				// Create the buffer
				full_buffer = CreateBuffer( txn->GetTransNumber(), true );
				buffer = CreateDynamicBuffer( field_list, false, txn->GetTransNumber() );

				// Second loop through records to fill in data values on the dynamic buffer
				data = message->data;
				for (int i=0; i<message->fieldcount; i++) {
					sql_query_field* fld = (sql_query_field*)data;
					data += sizeof( sql_query_field ) + fld->valuesize;

					// Grab the field values
					if (fld->valuesize) {
						char* tempstr = new char[ fld->valuesize + 1 ];
						memcpy( tempstr, fld->data, fld->valuesize );
						tempstr[ fld->valuesize ] = 0;

						buffer->GetField( fld->number )->SetString( tempstr );

						delete [] tempstr;
					};

					// Order by info
					if (fld->ordernumber != 0) {
						order_list[ ordercount ] = fld->number;
						if (fld->ordernumber < 0)
							orders[ ordercount ].direction = order_desc;
						ordercount++;
					};
				};

				// Now loop through the where clauses
				for (int i=0; i<wherecount; i++) {
					sql_query_where* where = (sql_query_where*)data;
					data += sizeof( sql_query_where ) + where->value1size + where->value2size;

					XCField* wherefield = full_buffer->GetField( where->number );
					if (!wherefield)
						RAISEFIELDERROR( ERR_INV_FIELD_NBR, where->number );

					wherefield = wherefield->Clone();
					where_list[ i ] = wherefield;

					// Grab the field value
					char* tempstr = new char[ where->value1size + 1 ];
					int size = where->value1size;
					memcpy( tempstr, where->data, size );
					tempstr[ size ] = 0;
					wherefield->SetString( tempstr );
					delete [] tempstr;

					// Set the comparison information
					XCCompare* comp = &comps[i];
					comp->comparison = (cmp_type)where->compnumber;
					comp->preparen = where->pre;
					comp->postparen = where->post;
					comp->field = wherefield;

					// Fix bug in comparison ordering
					comps[i+1].logical = (logic_oper)where->logical;

					if (where->compnumber == cmp_between) {
						// Special case for between comparison

						// Create the second value field
						wherefield = buffer->GetField( where->number )->Clone();
						comp->field2 = wherefield;

						char* tempstr = new char[ where->value2size + 1 ];
						int size = where->value2size;
						memcpy( tempstr, where->data + where->value1size, size );
						tempstr[ size ] = 0;
						wherefield->SetString( tempstr );
						delete [] tempstr;
					};
				};

				// Check to see that buffer is a database table
				if (buffer->GetType() != 0 && buffer->GetType() != 1)
					RAISETOOLSERROR( ERR_INV_BUFFER_TYPE );

				// Create the select statement
				if (recordcount == -1) {
					count_buffer = query.MakeCOUNT( buffer, NULL, txn->GetAction() == 1 );
				} else {
					if (txn->GetAction() == 1)
						query.MakeSELECTDISTINCT( buffer );
					else
						query.MakeSELECT( buffer );
				};

				// Add in any where clauses
				if (message->wherecount) {
					where_list[ message->wherecount ] = NULL;
					query.AddWHERE( where_list, comps );
				};

				// Add in any orderby clauses
				if (ordercount) {
					order_list[ ordercount ] = 0;
					query.AddORDERBY( order_list, orders );
				};
			};
		};

		temp = temp->next;
	};

	// If we didn't dynamically create a buffer, just create one using the table definition
	if (!buffer) {
		buffer = CreateBuffer( txn->GetTransNumber() );

		// Create a generic SQL select statement
		query.MakeSELECT( buffer );
	};

	// Submit the SQL statement
	dbtxn = GetDatabase()->CreateTxn();
	stmt = dbtxn->ExecuteSQL( &query );

	// Prepare the return messagelist
	txnreply = new XCTransactionInfo( this, txn->GetSource() );
	txnreply->SetReturnInfo( txn );
	txnreply->SetTransID( txn->GetTransID() );
	txnreply->SetTransNumber( txn->GetTransNumber() );
	txnreply->SetQuery();

	int counter = 0;
	int returned = 0;
	if (recordcount != -1) {
		// Create result messages
		sql_query_result_message* message = (sql_query_result_message*)new char[ 10240 ];
		message->data_type = QUERY_FIELD_INFO;
		while (!stmt->Eof() && !txn->abort && (!recordcount || returned < recordcount)) {
			if (counter >= recordstart) {
				char* data = message->data;
				message->fieldcount = buffer->GetFieldCount();
				message->order = counter;
				for (int i=0; i<message->fieldcount; i++) {
					long sizeleft = 10200 - (data - message->data);
					XCField* fld = buffer->GetFieldByIndex( i );
					sql_query_field* field = (sql_query_field*)data;
					field->number = fld->GetFieldNumber();
					field->valuesize = fld->charoutput( field->data, sizeleft, true );
					data += sizeof( sql_query_field ) + field->valuesize;
				};

				// Create the field data message
				long size = (data - message->data) + sizeof( sql_query_result_message );
				XCToolsMessage* reply = new XCToolsMessage( this );
				reply->CreateMsg( sizeof( sql_query_result_message ) + size );
				reply->SetHeader( msg_sql_query );

				sql_query_result_message* temp = (sql_query_result_message*)reply->GetDataPtr();
				memcpy( temp, message, size );

				// Add it to the return message
				txnreply->AddMessage( reply, QUERY_FIELD_INFO );

				returned++;
			};

			// Move on to the next record
			stmt->MoveNext();

			counter++;
		};

		// Cleanup
		delete [] (char*)message;

		// Now output the record count
		if (buffer->GetFieldCount() == 1) {
			count_buffer = query.MakeCOUNT( buffer, buffer->GetFieldByIndex(0), txn->GetAction() == 1 );
		} else {
			count_buffer = query.MakeCOUNT( buffer, NULL, txn->GetAction() == 1 );
		};

		// Add in any where clauses
		if (wherecount)
			query.AddWHERE( where_list, comps );

		// Find how many records we have
		dbtxn->ExecuteSQL( &query, SQL_NO_RESULT );
	};

	// We are doing a select count transaction
	txnreply->SetRecordCount( count_buffer->GetFieldByIndex( 0 )->GetLong( true ) );

	// End the database transaction
	dbtxn->EndTxn( true );

	// Cleanup where fields...
	for (int i=0; i<wherecount; i++) {
		where_list[ i ]->Release();

		if (comps[i].field2)
			comps[i].field2->Release();
	};

	// Cleanup arrays
	if (field_list)
		delete [] field_list;
	if (comps)
		delete [] comps;
	if (orders)
		delete [] orders;
	if (order_list)
		delete [] order_list;
	if (where_list)
		delete [] where_list;

	// Cleanup
	CleanupObjects( 3, &buffer, &stmt, &full_buffer );

	OUTFUNCRET( txnreply );
};



void XCAppInterface::OnUserMessage( char* message ) {
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "User message: %s", message );
};




void XCAppInterface::OnConnect( XCMsgPort* )
{
	INFUNC( XCAppInterface::OnConnect, this );
	
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "ERROR : XCAppInterface::OnConnect called" );
	RAISETOOLSERROR( -1 );

	OUTFUNC();
};


void XCAppInterface::SendTransaction( XCTransactionInfo* txn, XCMsgPortPtr dest, XCToolsMessage* reply, long app, route_loc src )
{
	INFUNC( XCAppInterface::SendTransaction, this );

	assert( appdic_header );
	if ( txn )
	{
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Total message count: %d", txn->GetCount() );

		// Set return information
		txn->SetReturnInfo( dest, reply );

		// send each of the reply messages
		XCMessageList* txnlist = txn->GetFirstMessage();
		while ( txnlist )
		{
			// Send this piece of information along
			txnlist->Send( txn );

			// Move on to next record
			txnlist = txnlist->next;
		};



		// create the header message (info)
		XCToolsMessage* header = new XCToolsMessage( this );

		long userlen = 0;
		if ( txn->GetUser() )
			userlen = strlen( txn->GetUser() );

		long passlen = 0;
		if ( txn->GetEncodedPass() )
			passlen = strlen( txn->GetEncodedPass() );

		if (txn->IsQuery())
			header->CreateMsg( SQL_QUERY_HEADER_SIZE + userlen + passlen + sizeof( trans_info_message ) + 1);
		else
			header->CreateMsg( TRANS_MESSAGE_SIZE + userlen + passlen + sizeof( trans_info_message ) + 1);

		if ( reply )
			header->SetReply( *reply );
		else {
			if (txn->IsQuery())
				header->SetHeader( msg_sql_query, src, dest_app, false, true );
			else
				header->SetHeader( msg_trans, src, dest_app, false, true );
		};

		trans_query_base* base = (trans_query_base*)header->GetDataPtr();
		base->appnbr = app;
		base->order = txn->GetMessageCount();
		base->transid = txn->GetTransID();

		if (txn->IsQuery()) {
			base->data_type = QUERY_HEADER_INFO;

//			printf( "SEND QUERY MESSAGE! Order=%d, ID=%d App=%d\n", base->order, base->transid, app );
			sql_query_header_message* query_info = (sql_query_header_message*)header->GetDataPtr();

			query_info->record_count = txn->GetRecordCount();
			query_info->start_record = txn->GetStartRecord();
			query_info->action = txn->GetAction();
			query_info->format = txn->GetTransNumber();

			if (userlen) {
				sprintf( query_info->data, "%s", txn->GetUser() );
				userlen++;
			};

			if (passlen) {
				sprintf( query_info->data + userlen, "%s", txn->GetEncodedPass() );
				passlen++;
			};

			query_info->passlen = passlen;
			query_info->userlen = userlen;
			query_info->session_id = txn->GetSession();
		} else {
			base->data_type = TRANS_HEADER_INFO;

			// If the job transaction flag is set, ensure that it gets passed through...
			if (txn->GetJob()) {
				base_message* msg_base = header->GetHeaderPtr();
				msg_base->miscflag |= misc_job_txn;
			};

			// Transaction specific stuff
			trans_message* trans_info = (trans_message*)header->GetDataPtr();
			trans_info->format = txn->GetTransNumber();

			// Fill in information specific to the info message
			trans_info_message* imsg = (trans_info_message*)trans_info->data;
			imsg->trans_number = txn->GetSuperID();
			imsg->action = txn->GetAction();
			imsg->success = txn->GetSuccess();
			imsg->phase = txn->GetPhase();
			imsg->lang = txn->GetLang();
			imsg->session_id = txn->GetSession();
			if (userlen) {
				sprintf( imsg->data, "%s", txn->GetUser() );
				userlen++;
			};

			if (passlen) {
				sprintf( imsg->data + userlen, "%s", txn->GetEncodedPass() );
				passlen++;
			};

			imsg->passlen = passlen;
			imsg->userlen = userlen;
			imsg->session_id = txn->GetSession();
		};

		SendMessage( msg_src, dest, header );
		delete header;
	};

	OUTFUNC();
};


void XCAppInterface::TransResponse( XCTransactionInfo* transin, const void* data, int size, int format, trans_data_type type ) {
	INFUNC( XCAppInterface::TransResponse, this );

	XCToolsMessage* reply = new XCToolsMessage( this );
	reply->CreateMsg( TRANS_MESSAGE_SIZE + size );
	reply->SetHeader( msg_trans_response );

	trans_message* trans_info = (trans_message*)reply->GetDataPtr();

	trans_info->data_type = type;
	trans_info->format = format;
	trans_info->order = transin->GetCount();
	trans_info->transid = transin->GetTransID();

	memcpy( trans_info->data, data, size );

	transin->AddMessage( reply, type );

	OUTFUNC();
};

void XCAppInterface::TransResponse( XCTransactionInfo* transin, XCBuffer* buffer, trans_data_type type ) {
	INFUNC( XCAppInterface::TransResponse, this );
	unsigned char *temp;
	long size;
	size = buffer->GetMsgSize();
	temp = new unsigned char[size];
	buffer->PutIntoMessage( temp, size );
	TransResponse( transin, temp, size, buffer->GetFormatNumber(), type );

	delete []temp;
	OUTFUNC();
};

void XCAppInterface::TransResponseMsg( XCTransactionInfo* transin, int number, const char* message, int buffer, int field, error_severity severity) {
	if (!message) {
		if (number == -1)
			message = "No message given (-1)";
		else
			message = "No message given";
	};
	int size = sizeof( trans_error_message ) + strlen( message ) + 1;
	trans_error_message *temp = (trans_error_message*)new char[ size ];

	temp->number = number;
	temp->severity = severity;
	temp->fieldnumber = field;
	strcpy( temp->text, message );

	((char*)temp)[size-1] = 0;
	TransResponse( transin, temp, size, buffer, TRANS_MESSAGE_TYPE );

	delete [] temp;
};


char* XCAppInterface::GetCatalogMsg( int number, int lang_id ) {
	INFUNC( XCAppInterface::GetCatalogMsg, this );

	XCFileTransport input( NULL );
	char filename[ MAX_PATH ];
	static buffer_msg_defn record;
	buffer_msg_hdr index_record;

	record.number = INT_MIN;

	sprintf( filename, "%sMESSAGES%d.Appdic", errloc, myappnum );

	// Open the file
	input.Open( filename, read_only, open_existing );

	// Read the index record, this tells us how many records are available
	input.Recv( &index_record, sizeof( buffer_msg_hdr ) );

	int messages = index_record.number;
	int seek = 0;

	// Allocate and read the indexes
	buffer_msg_hdr* hdr = new buffer_msg_hdr[ messages ];
	input.Recv( hdr, sizeof( buffer_msg_hdr ) * index_record.number );

	// Now find our message number in the index
	for (int i=0; i<messages; i++) {
		if (hdr[i].number == number) {
			seek = hdr[i].index;
			break;
		};
	};

	// Attempt to find the record
	input.Seek( seek );
	try {
		while (!input.Eof() && number >= record.number) {
			input.Recv( &record, sizeof( buffer_msg_defn ) );
			if (record.number == number && record.lang_id == lang_id) {
				input.Close();
				delete [] hdr;
				return strdup( record.message );
			};
		};
	} catch( ... ) {};

	delete [] hdr;
	input.Close();

	OUTFUNCRET( NULL );
};





void XCAppInterface::GetOptions( XCAppOptions& res_options ) {
	res_options = options;
};


void XCAppInterface::SetOptions( XCAppOptions& new_options ) {
	options = new_options;
};

XCMsgPortPtr XCAppInterface::GetParent() {
	return parent;
};

void XCAppInterface::SetParent( XCMsgPortPtr new_parent ) {
	if (parent)
		delete parent;

	parent = new_parent;
	parent->porttype = pt_misc;
	parent->ready = true;
	parentpid = 0;
	mType = dest_gcs;

	if (childlist == new_parent) {
		// Remove the parent from the child list
		childlist = childlist->next;
	};
};




const char* XCAppInterface::GetLocale() {
	long lang = XCGetLanguage();

	language_defn* ld = (language_defn*)FixOffset( appdic_header->languages.offset );
	int count = appdic_header->languages.count;

	while (count && ld->id != lang) {
		ld++;
		count--;
	};

	if (count)
		return ld->name;
	else
		return NULL;
};




XCField* XCAppInterface::GetLiteral( long LiteralNum ) {
	INFUNC( XCAppInterface::GetLiteral, this );
	long lang = XCGetLanguage();

	assert( lang >= 0 );
	assert( lang < MAX_LANGUAGES );
	assert( literal_values_buf[ lang ] );

	OUTFUNCRET( literal_values_buf[ lang ]->GetField( LiteralNum ) );
};


XCField* XCAppInterface::GetSysValue( long SysValNum ) {
	INFUNC( XCAppInterface::GetSysValue, this );
	assert( system_values_buf );
	OUTFUNCRET( system_values_buf->GetField( SysValNum ) );
};








lit_group* XCAppInterface::GetLitGroup( int number ) {
	assert( appdic_header );
	lit_group* group = (lit_group*)FixOffset( appdic_header->litgroups.offset );

	// Find the group record
	int count = appdic_header->litgroups.count;
	while (count && group->number != number) {
		count--;
		group++;
	};

	if (count)
		return group;
	else
		return NULL;
};

XCField* XCAppInterface::FromLitGroup( long value, long groupnbr ) {
	lit_group* group = GetLitGroup( groupnbr );
	if (group) {
		// Now that we have the group record, find the group value
		int count = group->literals.count;
		lit_relate* relate = (lit_relate*)FixOffset( group->literals.offset );
		while (count && relate->value != value) {
			count--;
			relate++;
		};

		if (count)
			return GetLiteral( relate->lit_number );
		else
			return NULL;
	} else
		return NULL;
};




long XCAppInterface::ToLitGroup( const char* value, long groupnbr ) {
	lit_group* group = GetLitGroup( groupnbr );
	if (group) {
		int count;

		// Now that we have the group record, find the group value
		count = group->literals.count;
		lit_relate* relate = (lit_relate*)FixOffset( group->literals.offset );
		XCField* litval = GetLiteral( relate->lit_number );
		while (count && strcmp( litval->GetStringPtr(), value ) != 0) {
			count--;
			relate++;
			litval = GetLiteral( relate->lit_number );
		};

		if (count)
			return relate->value;
		else
			return NOT_IN_LIT_GROUP;
	} else
		return NOT_IN_LIT_GROUP;
};



long XCAppInterface::ToLitGroup( const XCField* value, long groupnbr ) {
	lit_group* group = GetLitGroup( groupnbr );
	if (group) {
		int count;

		// Now that we have the group record, find the group value
		count = group->literals.count;
		lit_relate* relate = (lit_relate*)FixOffset( group->literals.offset );
		XCField* litval = GetLiteral( relate->lit_number );
		while (count && litval->CompareField( value ) != 0) {
			count--;
			relate++;
			litval = GetLiteral( relate->lit_number );
		};

		if (count)
			return relate->value;
		else
			return NOT_IN_LIT_GROUP;
	} else
		return NOT_IN_LIT_GROUP;
};





void XCAppInterface::CleanupObjects( int bufcount, ... ) {
	INFUNC( XCAppInterface::CleanupObjects, this );

	va_list parmlist;
	XCBase** temp;

	// Read our arguments
	va_start( parmlist, bufcount );
	for (int i=0; i<bufcount; i++) {
		temp = va_arg( parmlist, XCBase** );
		if (*temp) {
//			ToolsTrace( TRC_APPINTERFACE, TLV_EVERYTHING, "Clean buffer: %d", i+1 );
			(*temp)->Release();
			*temp = NULL;
		};
	};
	va_end( parmlist );

	OUTFUNC();
};


XCTxn* XCAppInterface::GetSuper( long number ) {
	superlist* temp = supertrans;
	while (temp && temp->id != number)
		temp = temp->next;

	if (temp)
		return temp->trans;
	else
		return NULL;
};

long XCAppInterface::GetSuper( XCTxn* find ) {
	superlist* temp = supertrans;
	while (temp && temp->trans != find)
		temp = temp->next;

	if (temp)
		return temp->id;
	else
		return 0;
};


superlist* XCAppInterface::AddSuper() {
	superlist* temp = new superlist;

	temp->id = rand();
	temp->trans = new XCTxn( NULL, db );
	temp->next = supertrans;

	supertrans = temp;
	return temp;
};




XCDBDriverDescr* XCAppInterface::GetDBDriverDesc( XCBuffer* buffer ) {
	INFUNC( XCAppInterface::GetDBDriverDesc, this );

	assert( buffer );
	assert( buffer->mydesc );
	assert( buffer->mydesc->dd_offset );

	// Get pointer to table information
	buffer_db_tables* table = (buffer_db_tables*)FixOffset( buffer->mydesc->dd_offset );
	assert( table );

//ToolsTrace( TRC_APPINTERFACE, TLV_EVERYTHING, "===GetDBDriverDesc Table DB_Offset : %ld, Format : %ld, View_Flag : %ld, Relate_Count : %ld, Relate_Offset : %ld", table->db_offset, table->format, table->view_flag, table->relate_count, table->relate_offset );

	// Get database information from table information
	buffer_db_defn* db = (buffer_db_defn*)FixOffset( table->db_offset );
	assert( db );

//ToolsTrace( TRC_APPINTERFACE, TLV_EVERYTHING, "===GetDBDriverDesc Number : %ld, Name : %s, User : %s, Pass : %s, Type : %ld, Table_Count : %ld, Table_Offset : %ld", db->number, db->name, db->user, db->pass, db->type, db->table_count, db->table_offset );

	// Get database connection
	XCDBInterface* dbi = GetDatabase()->GetInterface( db->number );
	OUTFUNCRET( dbi->GetDatabaseInfo() );
};




void XCAppInterface::DelTxnFragment( long trans_id ) {
	TxnListLock.Lock();

	// Find which transaction to remove
	XCTransactionInfo* prev;
	XCTransactionInfo* txn = FindTxn( trans_id, &prev );

	if (txn) {
		// If the transaction is not in use, delete it
		if (!txn->in_use) {
			if (!prev) {
				// First one in the list
				TxnList = txn->next;
			} else {
				// Somewhere in the middle/end
				prev->next = txn->next;
			};

			trans_open--;
			txn->Release();
		} else {
			// Otherwise, mark the transaction for abort
			txn->abort = true;
		};
	};

	TxnListLock.UnLock();
};



//
// Assumption: TRANSACTION LIST IS ALREADY LOCKED!!!!!!
//
XCTransactionInfo* XCAppInterface::FindTxn( long trans_id, XCTransactionInfo** prev ) {
	// Attempt to locate a valid transaction
	if (prev)
		*prev = NULL;

	XCTransactionInfo* txn = TxnList;
	while ( txn && trans_id != txn->GetTransID() ) {
		if (prev)
			*prev = txn;
		txn = txn->next;
	};

	return txn;
};


XCTransactionInfo* XCAppInterface::AddTxn( XCToolsMessage* msg, XCMsgPort* port ) {
	bool found = false;
	XCTransactionInfo* result = NULL;

	trans_query_base* trans = (trans_query_base*)msg->GetDataPtr();

	TxnListLock.Lock();

	XCTransactionInfo* txn = FindTxn( trans->transid );
	if (!txn) {
		// Add it to the transaction list
		trans_open++;
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Create Transaction" );
		txn = TxnList;
		if ( txn ) {
			while ( txn->next )
				txn = txn->next;
			txn->next = new XCTransactionInfo( this, port );
			txn = txn->next;
		} else {
			TxnList = new XCTransactionInfo( this, port );
			txn = TxnList;
		};
		txn->SetTransID( trans->transid );
		txn->credentials = NULL;
	};
	
	// Decide what type of message we have	
	if ( trans->data_type == TRANS_HEADER_INFO ) {
		// Parse Header Info message
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Txn Msg : Header info" );
		txn->AddHeader( msg, trans->data_type );
	} else if (trans->data_type == QUERY_HEADER_INFO) {
		// Parse Header Info message
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "SQL Query Msg : Header info" );
		txn->AddHeader( msg, trans->data_type );
	} else {
		// Add message to XCTransactionInfo
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Txn Msg : Detail info" );
		txn->AddMessage( msg, trans->data_type );
	};

	// Check transaction (with received header) for completion
	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Trans num: %d  %d == %d", txn->GetTransNumber(), txn->GetTotalCount(), txn->GetCount() );
	if ( txn->GetTransNumber() && txn->GetTotalCount() == txn->GetCount() ) {
		txn->in_use = true;
		result = txn;
	};

	TxnListLock.UnLock();
	return result;
};


void XCAppInterface::DelTxn( XCTransactionInfo* txn ) {
	TxnListLock.Lock();

	// Find which transaction to remove
	XCTransactionInfo* prev;
	FindTxn( txn->GetTransID(), &prev );

	if (!prev) {
		// First one in the list
		TxnList = txn->next;
	} else {
		// Somewhere in the middle/end
		prev->next = txn->next;
	};

	trans_open--;
	trans_complete++;

	TxnListLock.UnLock();

	txn->Release();
};



long XCAppInterface::GetAppNumber() const {
	return myappnum;
};

const char* XCAppInterface::GetProcessName() const {
	return process;
};
